package com.ehotting.mqbd.core.manager;


import com.alibaba.fastjson.JSON;
import com.ehotting.eaf.core.utils.AssertUtils;
import com.ehotting.mqbd.api.bean.solver.*;
import com.ehotting.mqbd.api.enums.ServerAvailableStateEnum;
import com.ehotting.mqbd.api.enums.ServerWorkStateEnum;
import com.ehotting.mqbd.core.util.AgentIdGenUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class SolverStateManager {

    private SolverInfoManager solverInfoManager;

    private boolean isRun = true;

    private static final long WAIT_TIME = 8000;

    public SolverStateManager(SolverInfoManager solverInfoManager){
        this.solverInfoManager = solverInfoManager;
    }

    /***
     * 绑定AgentId的求解器的循环等待
     * @param module
     * @param agentId
     * @return
     */
    public SolverStateBean getFreeAndAvailableByModuleAndCirculate(String module, String agentId){
        SolverStateBean solverState = null;
        int times = 0;
        while (solverState==null  && isRun){

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            solverState = this.getSolverStateBindAgentId(module,agentId);

            if(solverState == null){
                log.info("Get solver[agentId:{},times:{}] is null,break.",
                        agentId,
                        times++
                        );
                break;
            }

            if(solverState.getAvailableState() == ServerAvailableStateEnum.NOT ){
                log.info("Get solver[agentId:{},times:{}] state is unavailable[{}],break.",
                        agentId,
                        times++,
                        solverState.getAvailableState());
                solverState = null;
                break;
            }

            if(solverState.getWorkingState() == ServerWorkStateEnum.BUSY){
                log.info("Get solver[agentId:{},times:{}] state is workState[{}] ,continue.",
                        agentId,
                        times++,
                        solverState.getWorkingState());

                solverState = null;
                try {
                    Thread.sleep(WAIT_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                continue;
            }
            log.info("________________________GET SOLVER________________________");
            log.info("Bind solver[agentId:{}]:{} is available",agentId,JSON.toJSONString(solverState));

        }
        return solverState;
    }



    public SolverStateBean getFreeAndAvailableByModuleAndCirculate(String module){
        int times = 0;
        SolverStateBean solverState = null;
        while (solverState==null && isRun){
            List<SolverStateBean> solverStateList = solverInfoManager.getByModule(module);

            log.info("Get solverStateList.size:{} by module[{}].",solverStateList.size(),module);
            if(solverStateList.size() > 0 ){
                solverState = getFreeAndAvailable(solverStateList);
            }

            if(solverState==null){
                log.info("Get available solver[times:{},module:{}] is null,continue.",
                        times++,
                        module);
                try {
                    Thread.sleep(WAIT_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        return solverState;
    }

    public SolverStateBean getFreeAndAvailable(List<SolverStateBean> solverStateList){
        SolverStateBean solverState = solverStateList
                .stream()
                .filter(t-> {return
                        t.getAvailableState()== ServerAvailableStateEnum.YES
                                && t.getWorkingState() != ServerWorkStateEnum.BUSY;
                })
                .sorted(Comparator.comparing(SolverStateBean::getSolvingCount))
                .sorted(Comparator.comparing(SolverStateBean::getRequestCount))
                .findFirst()
                .orElse(null);

        return solverState;
    }

    public SolverStateBean getAvailableAndLessRequestCount(String module){
        List<SolverStateBean> solverStateList = solverInfoManager.getByModule(module);
        return getAvailableByOrder(solverStateList);

    }

    public String getAndBinByModelAndAgentIdOrDefault(String agentId){

        SolverStateBean solverState = getAvailableByAgentId(agentId);

        if(solverState == null) {
            String module = AgentIdGenUtil.getModule(agentId);
            solverState = getAvailableAndLessRequestCount(module);
        }

        if(solverState!=null){
            solverInfoManager.setSolverRefAgentId(agentId,solverState.getSolverInfo());
        }

        String ip = solverState!=null && solverState.getSolverInfo()!=null ?solverState.getSolverInfo().getIp():"";

        return ip;
    }


    public SolverStateBean getAvailableByOrder(List<SolverStateBean> solverStateList){
        SolverStateBean solverState = solverStateList
                .stream()
                .filter(t-> {return
                        t.getAvailableState()== ServerAvailableStateEnum.YES;
                })
                .sorted(Comparator.comparing(SolverStateBean::getSolvingCount))
                .sorted(Comparator.comparing(SolverStateBean::getRequestCount))
                .collect(Collectors.toList())
                .stream()
                .findFirst()
                .orElse(null);

        return solverState;
    }

    public SolverStateBean getAvailableByCirculateAnd3Times(String module){
        int times = 0;
        SolverStateBean solverState = null;
        while (solverState==null && times<3 &&  isRun){
            List<SolverStateBean> solverStateList = solverInfoManager.getByModule(module);

            log.info("Get solverStateList.size:{} by module[{}].",solverStateList.size(),module);

            if(solverStateList != null && solverStateList.size() >0 ){
                solverState = getAvailableByOrder(solverStateList);
            }
            if(solverState==null){
                log.info("Get available mqbd[times:{},module:{}] is null,continue.",
                        times++,
                        module);
                try {
                    Thread.sleep(WAIT_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.info("module[{}] solverStateList.size()=0,continue.",module);
        }
        return solverState;
    }

    public SolverStateBean getSolverStateBindAgentId(String module, String agentId){
        SolverInfoBean solverInfo = solverInfoManager.getSolverRefAgentId(agentId);
        if(solverInfo==null){
            log.info("Get solverInfo bind agentId[{}] is null by module,return.",agentId );
            return null;
        }
        log.info("Get solverInfo:{} by agentId:{}", JSON.toJSONString(solverInfo),agentId);

        SolverStateBean solverState =  solverInfoManager.get(module,solverInfo.getIp());

        return solverState;
    }


    public SolverStateBean getAvailableByAgentId(String agentId){
        String module = AgentIdGenUtil.getModule(agentId);
        SolverInfoBean solverInfo = solverInfoManager.getSolverRefAgentId(agentId);
        if(solverInfo==null){
            log.info("Get solverInfo bind agentId[{}] is null by module,return.",agentId );
            return null;
        }
        log.info("Get solverInfo:{} agentId:{}", JSON.toJSONString(solverInfo),agentId);

        List<SolverStateBean> solverStateList = solverInfoManager.getByModule(module);
        log.info("solverStateList.size():{}",solverStateList.size());
        SolverStateBean solverState = solverStateList
                .stream()
                .filter(t-> {return
                        t.getAvailableState()== ServerAvailableStateEnum.YES
                                && (t.getSolverInfo().equals(solverInfo));
                })
                .sorted(Comparator.comparing(SolverStateBean::getSolvingCount))
                .findFirst()
                .orElse(null);
        return solverState;
    }


    public List<SolverStateBean> findByModule(String module){
        List<SolverStateBean> solverStateList = solverInfoManager.getByModule(module);
        return solverStateList;
    }



    public void setSolverBusy(SolverStateBean solverState){
        solverState = solverInfoManager.get(solverState.getModule(),solverState.getSolverInfo().getIp());
        solverState.addRequestCount();
        solverState.addSolvingCount();
        saveStatue( solverState);
    }

    public void addSolverRequest(SolverStateBean solverState){
        solverState = solverInfoManager.get(solverState.getModule(),solverState.getSolverInfo().getIp());
        solverState.addRequestCount();
        saveStatue( solverState);
    }


    public void setSolverFree(String module, String ip){
        SolverStateBean solverState = solverInfoManager.get(module,ip);
        if(solverState!=null){
            solverState.minusSolvingCount();
            saveStatue( solverState);
        }

    }



    public void setSolverFree(SolverStateBean solverState){
        solverState = solverInfoManager.get(solverState.getModule(),solverState.getSolverInfo().getIp());
        if(solverState!=null) {
            solverState.minusSolvingCount();
            saveStatue(solverState);
        }
    }

    public void setSolverAvailable(SolverStateBean solverState){
        solverState = solverInfoManager.get(solverState.getModule(),solverState.getSolverInfo().getIp());
        if(solverState!=null) {
            solverState.setAvailable();
            saveStatue(solverState);
        }
    }

    public void setSolverStop(SolverStateBean solverState){
        solverState = solverInfoManager.get(solverState.getModule(),solverState.getSolverInfo().getIp());
        if(solverState!=null) {
            solverState.setStop();
            saveStatue(solverState);
        }
    }

    public SolverStateBean getByModuleAndIp(String module, String ip){
        List<SolverStateBean> solverStateList = findByModule( module);
        SolverStateBean solverState = solverStateList
                .stream()
                .filter(t-> {return t.getSolverInfo().getIp().equals(ip);
                })
                .findFirst()
                .orElse(null);
        return solverState;
    }

    public void release(String module, String ip){
        List<SolverStateBean> solverStateList = findByModule(module);
        solverStateList.stream().forEach(t->{
            if(t.getSolverInfo().getIp().equals(ip)){
                t.setSolvingCount(0);
                saveStatue(t);
            }
        });
    }

    public void live(String module, String ip){
        List<SolverStateBean> solverStateList = findByModule(module);
        solverStateList.stream().forEach(t->{
            if(t.getSolverInfo().getIp().equals(ip)){
                t.setAvailable();
                t.setSolvingCount(0);
                saveStatue(t);
            }
        });
    }

    public void saveStatue(SolverStateBean solverState){
        solverInfoManager.save(solverState);
    }



    public void saveSolverStop(String module, String ip){
        SolverStateBean solverState = solverInfoManager.get(module,ip);
        if(solverState!=null){
            solverState.setStop();
            saveStatue( solverState);
        }
    }

    public void saveSolverAvailable(String module, String ip, String port){
        SolverStateBean solverState = solverInfoManager.get(module,ip);
        if(solverState!=null){
            solverState.setAvailable();
            saveStatue(solverState);
        }
    }

    public void shutdown(){
        this.isRun = false;
    }

}